React 16 Upgrade

New Lifecycle

Mounting
  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()
  • UNSAFE_componentWillMount()

Updating
  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()
  • UNSAFE_componentWillReceiveProps()
  • UNSAFE_componentWillUpdate()

Previously, it was only called if the component was re-rendered by its parent,
and would not fire as the result of a local setState.

// Ok in react 16.3 but Bug in react16.4
static getDerivedStateFromProps(props, state) {
  if (props.value !== state.controlledValue) {
    return {
      // Since this method fires on both props and state changes, local updates
      // to the controlled value will be ignored, because the props version
      // always overrides it. Oops!
      controlledValue: props.value,
    };
  }
  return null;
}

// Fixed in react16.4
static getDerivedStateFromProps(props, state) {
  const prevProps = state.prevProps;
  // Compare the incoming prop to previous prop
  const controlledValue =
    prevProps.value !== props.value
      ? props.value
      : state.controlledValue;
  return {
    // Store the previous props in state
    prevProps: props,
    controlledValue,
  };
}

getSnapshotBeforeUpdate(prevProps, prevState) {
  if (prevProps.list.length < this.props.list.length) {
    const list = this.listRef.current;
    return list.scrollHeight - list.scrollTop;
  }
  return null;
}

componentDidUpdate(prevProps, prevState, snapshot) {
  if (snapshot !== null) {
    const list = this.listRef.current;
    list.scrollTop = list.scrollHeight - snapshot;
  }
}

Unmounting
  • componentWillUnmount
Error Handling
  • componentDidCatch

class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    componentDidCatch(error, info) {
        // Display fallback UI
        this.setState({ hasError: true });
        logErrorToMyService(error, info);
    }

    render() {
        if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
    }
}

Then you can use it as a regular component:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

Context API

API

  • React.createContext()
  • Provider
  • Consumer

// AppContext.js
export const AppContext = React.createContext({
  count: 0
});

const withApp = (Component) => (props) => (
  <Consumer>
    {value => <Component {...props} count={value.count}/>}
  </Consumer>
);

// CountButton.js
const {Consumer} = AppContext;
// @withApp Decorator in ES2016
class CountButton extends Component {
  render() {
    return(
      <button type="button" {...this.props}>{this.props.count}</button>
    );
  }
});
export default = withApp(CountButton);

// App.js
const {Provider} = AppContext;
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  add = () => {
    this.setState({
      count: this.state.count + 1
    });
  };

  render() {
    return (
      <div>
        <Provider value={this.state}>
          <CountButton onClick={this.add}/>
        </Provider>
        <CountButton onClick={this.add}/>
      </div>
    );
  }
}

New Render Types

//string
render(){
  return 'hello,world'
}
 
//number
render(){
  return 12345
}
 
//boolean
render(){
  return isTrue?true:false
}
 
//null
render(){
  return null
}
 
//arrays ,need key . (fragments)
render(){
  return [
    <div>hello</div>,
    <span>world</span>,
    <p>oh</p>
  ]
}

//portals
...

Fragments

A common pattern in React is for a component to return multiple elements.
Fragments let you group a list of children without adding extra nodes to the DOM.

render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}

Portals

// React mounts a new div and renders the children into it
return (
  <div>
    {this.props.children}
  </div>
);

const modalRoot = document.getElementById('modal-root');
// Modal.js
constructor(props) {
  super(props);
  this.el = document.createElement('div');
}

componentDidMount() {
  modalRoot.appendChild(this.el);
}

componentWillUnmount() {
  modalRoot.removeChild(this.el);
}

render() {
  // React does *not* create a new div. It renders the children into `domNode`.
  // `domNode` is any valid DOM node, regardless of its location in the DOM.
  return ReactDOM.createPortal(
    this.props.children,
    this.el
  );
}

CreateRef & ForwardRef

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} 
          className="FancyButton" 
          onClick={props.onClick}>
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }

  handleClick = () => {
    // ref.current -> dom
    this.myRef.current.focus();
  };

  render() {
    return (
      <FancyButton ref={this.myRef} 
                   onClick={() => console.log(this.myRef.current)}>
        Click me!
      </FancyButton>
    );
  }
}
// <button class="FancyButton">Click me!</button>

SetState()

  • Calling setState with null no longer triggers an update.
  • Calling setState directly in render always causes an update.
  • setState callbacks (second argument) now fire immediately after componentDidMount / componentDidUpdate instead of after all components have rendered.

DOM Attributes

In the past, React used to ignore unknown DOM attributes.
Now, any unknown attributes will end up in the DOM.

// Your code:
<div mycustomattribute="something" />
// React 15 output:
<div />
// React 16 output:
<div mycustomattribute="something" />

You should still use the canonical React naming for known attributes.

// Yes, please
<div tabIndex="-1" />
// Warning: Invalid DOM property `tabindex`. Did you mean `tabIndex`?
<div tabindex="-1" />

Pointer Events (React 16.4)

The following event types are now available in React DOM:

  • onPointerDown
  • onPointerMove
  • onPointerUp
  • ……

These events will only work in browsers that support the Pointer Events specification.


Awbeci
3.1k 声望212 粉丝

Awbeci